package cn.kgc.advice;

import cn.kgc.annotation.Logical;
import cn.kgc.annotation.LoginType;
import cn.kgc.annotation.RequireLogin;
import cn.kgc.excpetion.GraceException;
import cn.kgc.grace.result.ResponseStatusEnum;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import java.lang.reflect.Method;
import java.util.Arrays;

import static cn.kgc.base.SystemConstant.HEADER_LOGIN_TYPE;

/**
 * @author wangyue
 * @version 1.0
 * 验证令牌中的登录类型是否与控制器中允许访问的登录类型相等
 */
@Aspect
@Component
public class LoginPermissionAdvice {
    @Pointcut("execution(* cn.kgc.controller..*(..))")
    public void pointcut1(){}

    @Around("pointcut1()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable{
        //获得控制器中的目标方法
        Method method= ((MethodSignature)joinPoint.getSignature()).getMethod();
        //获得用户请求头中的登录类型
        ServletRequestAttributes attributes= (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        String loginType = attributes.getRequest().getHeader(HEADER_LOGIN_TYPE);
        //如果请求头中登录类型与方法注解中的登录类型不匹配
        if(!checkLoginPermission(method,loginType)){
            //请求被拒绝,抛出异常
            GraceException.display(ResponseStatusEnum.NO_AUTH);
        }
        //否则请求放行
        return joinPoint.proceed();
    }

    private boolean checkLoginPermission(Method method,String loginType){
        //如果方法上没有注解,说明此方法任意登录方式令牌都可以访问
        if(!method.isAnnotationPresent(RequireLogin.class)){
            return true;
        }
        //如果方法上有注解,判断登录类型是否与请求头中的类型匹配
        RequireLogin annotation = method.getAnnotation(RequireLogin.class);
        //获得方法允许访问的所有令牌的登录类型
        LoginType[] value = annotation.value();
        //获得类型的逻辑关系
        Logical logical= annotation.logical();
        //根据类型的逻辑关系判断
        if(logical==Logical.AND){
            //登录令牌的登录类型(方法中的传参loginType)要与方法注解上指定的类型都匹配,请求才能被访问。
            return Arrays.stream(value).allMatch(type->type.getValue().equals(loginType));
        }
        //登录令牌的登录类型(方法中的传参loginType)要与方法注解上指定的类型中任意一个匹配,请求就能被访问。
        return  Arrays.stream(value).anyMatch(type-> type.getValue().equals(loginType));
    }
}